﻿using DapperExtensions.Mapper;
using DapperExtensions.Sql;
using Microsoft.Extensions.Options;
using System;
using System.Data;
using System.Threading.Tasks;

namespace DapperExtensions
{
    public partial interface IDatabase : IDisposable
    {
        bool HasActiveTransaction { get; }
        IDbConnection Connection { get; }
        void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted);
        void Commit();
        void Rollback();
        void RunInTransaction(Action action,IsolationLevel level = IsolationLevel.RepeatableRead);
        T RunInTransaction<T>(Func<T> func, IsolationLevel level = IsolationLevel.RepeatableRead);
        Task RunInTransactionAsync(Func<Task> func, IsolationLevel level = IsolationLevel.RepeatableRead);
        Task<T> RunInTransactionAsync<T>(Func<Task<T>> func, IsolationLevel level = IsolationLevel.RepeatableRead);
        void ClearCache();
        Guid GetNextGuid();
        IClassMapper GetMap<T>() where T : class;
    }

    public partial class Database : IDatabase
    {

        private IDapperImplementor _dapper;

        private IDbTransaction _transaction;
        //是否回收完毕
        bool _disposed;
        public Database(IDbConnection connection, ISqlGenerator sqlGenerator,IDapperImplementor dapper)
        {
            _dapper = dapper;//new DapperImplementor(sqlGenerator);
            Connection = connection;

            if (Connection.State != ConnectionState.Open)
            {
                Connection.Open();
            }
        }
        public Database(IOptions<DataBaseOptions> options, IDapperExtensionsConfiguration Configuration,IDapperImplementor dapper)
        {
            _dapper = dapper; // new DapperImplementor(new SqlGeneratorImpl(Configuration));
            Connection = options.Value.DbConnection();

            if (Connection.State != ConnectionState.Open)
            {
                Connection.Open();
            }
        }

        public bool HasActiveTransaction
        {
            get
            {
                return _transaction != null;
            }
        }

        public IDbConnection Connection { get; private set; }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this); //标记gc不在调用析构函数
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed) return; //如果已经被回收，就中断执行
            if (disposing)
            {
                //释放本对象中管理的托管资源
                _dapper = null;
            }
            //释放非托管资源
            if (Connection.State != ConnectionState.Closed)
            {
                if (_transaction != null)
                {
                    _transaction.Rollback();
                    _transaction.Dispose();
                }

                Connection.Close();
            }
            _disposed = true;
        }
        ~Database()
        {
            Dispose(false);
        }
        public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
        {
            _transaction = Connection.BeginTransaction(isolationLevel);
        }

        public void Commit()
        {
            _transaction.Commit();
            _transaction = null;
        }

        public void Rollback()
        {
            _transaction.Rollback();
            _transaction = null;
        }

        public void RunInTransaction(Action action, IsolationLevel level = IsolationLevel.RepeatableRead)
        {
            BeginTransaction(level);
            try
            {
                action();
                Commit();
            }
            catch
            {
                if (HasActiveTransaction)
                {
                    Rollback();
                }

                throw;
            }
        }

        public T RunInTransaction<T>(Func<T> func, IsolationLevel level = IsolationLevel.RepeatableRead)
        {
            BeginTransaction(level);
            try
            {
                T result = func();
                Commit();
                return result;
            }
            catch
            {
                if (HasActiveTransaction)
                {
                    Rollback();
                }

                throw;
            }
        }
        public async Task RunInTransactionAsync(Func<Task> func, IsolationLevel level = IsolationLevel.RepeatableRead)
        {
            BeginTransaction(level);
            try
            {
                var task = func();
                await task;
                Commit();
            }
            catch
            {
                if (HasActiveTransaction)
                {
                    Rollback();
                }
                throw;
            }
        }
        public async Task<T> RunInTransactionAsync<T>(Func<Task<T>> func,IsolationLevel level=IsolationLevel.RepeatableRead)
        {
            BeginTransaction(level);
            try
            {
                var result = await func();
                Commit();
                return result;
            }
            catch
            {
                if (HasActiveTransaction)
                {
                    Rollback();
                }

                throw;
            }
        }

        public void ClearCache()
        {
            _dapper.SqlGenerator.Configuration.ClearCache();
        }

        public Guid GetNextGuid()
        {
            return _dapper.SqlGenerator.Configuration.GetNextGuid();
        }

        public IClassMapper GetMap<T>() where T : class
        {
            return _dapper.SqlGenerator.Configuration.GetMap<T>();
        }
    }

    public class DataBaseOptions
    {
       public ISqlDialect SqlDialect { get; set; }

        public Func<IDbConnection> DbConnection { get; set; }
    }
}
